 /* 
  * Copyright (C) 2012 maxim 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  * USA
  */

#include "user_threads.h"
#include "user_mutex.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "tests.h"

/*
 * Basic mutex test
 */


uthread_mutex_t mutex1;
uthread_mutex_t mutex2;
uthread_mutex_t mutex3;


int thread3(void* arg)
{
	msg_with_expected_order(5, "Thread 3 runs for first time");

	/* can't unlock a lock that belongs to other thread */
	assert (uthread_mutex_unlock(mutex1) == MUTEX_LOCKED);


	assert (uthread_mutex_lock(mutex2) == MUTEX_SUCCESS);
	msg_with_expected_order(6, "Thread 3, got lock2, sleeping");
	uthread_yield();
	delay(10000000, 0);
	msg_with_expected_order (8, "Thread 3, exit should drop mutex2");

	/* Don't unlock mutex2, it will be unlocked automatically */
	uthread_exit(0);
	assert(0);
}


int thread2(void* arg)
{
	/* take the lock - should be done instantly */

	msg_with_expected_order(3, "Thread 2 runs for first time");
	assert (uthread_mutex_try_lock(mutex1) == MUTEX_SUCCESS);
	msg_with_expected_order (4, "Thread 2, got lock");

	assert (uthread_create(thread3, NULL, 4) == 3);
	uthread_yield();

	msg_with_expected_order(7, "Thread 2 now will sleep for the lock");

	/* Wait for thread 3 */
	assert (uthread_mutex_lock(mutex2) == MUTEX_SUCCESS);

	msg_with_expected_order (9, "Thread 2, got lock 2, sleeping");
	delay(10000, 0);
	assert (uthread_mutex_unlock(mutex2) == MUTEX_SUCCESS);

	/* Don't unlock mutex1 - it will be unlocked automatically*/
	return 0;
}

int thread1(void* arg)
{
	msg_with_expected_order(2, "Thread1 runs first time, creates thread 2");
	assert (uthread_create(thread2, NULL, 1) == 2);
	uthread_yield();

	/* Wait till thread 2 releases the lock
	 * We take lock twice*/
	assert (uthread_mutex_lock(mutex1) == MUTEX_SUCCESS);
	assert (uthread_mutex_lock(mutex1) == MUTEX_FAILURE);

	msg_with_expected_order(10, "Thread 1, got lock");

	/* Now release it again twice */
	assert (uthread_mutex_unlock(mutex1) == MUTEX_SUCCESS);
	assert (uthread_mutex_unlock(mutex1) == MUTEX_UNLOCKED);

	/* That ought to fail */
	assert (uthread_mutex_unlock(mutex1) == MUTEX_UNLOCKED);

	/* And take all the locks again */
	assert(uthread_mutex_lock(mutex1) == MUTEX_SUCCESS);
	assert(uthread_mutex_lock(mutex2) == MUTEX_SUCCESS);
	assert(uthread_mutex_lock(mutex3) == MUTEX_SUCCESS);

	msg_with_expected_order(11, "Thread 1, exiting");

	return 0;
}

void test2() {

	reset_counter();
	msg("\nRunning test #2...");

	assert(uthread_mutex_init(&mutex1) == UTHREAD_SUCCESS);
	assert(uthread_mutex_init(&mutex2) == UTHREAD_SUCCESS);
	assert(uthread_mutex_init(&mutex3) == UTHREAD_SUCCESS);


	msg_with_expected_order(1, "Starting test");
	assert (uthread_create(thread1, NULL, 2) == 1);
	uthread_yield();
	assert (uthread_join(2, NULL) == UTHREAD_SUCCESS);
	assert (uthread_join(1, NULL) == UTHREAD_SUCCESS);

	// to cleanup this thread
	assert (uthread_join(3, NULL) == UTHREAD_SUCCESS);


	assert(uthread_mutex_lock(mutex3) == MUTEX_SUCCESS);
	assert(uthread_mutex_destroy(mutex3) == MUTEX_LOCKED);
	assert (uthread_mutex_unlock(mutex3) == MUTEX_SUCCESS);
	assert(uthread_mutex_destroy(mutex3) == MUTEX_SUCCESS);



}
